/**
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
 */

package net.sourceforge.pmd.lang.java.rule.codestyle;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration;
import net.sourceforge.pmd.lang.java.ast.internal.ImportWrapper;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;

@Deprecated
public class DuplicateImportsRule extends AbstractJavaRule {
    private static final Logger LOG = Logger.getLogger(DuplicateImportsRule.class.getName());

    private Set<ImportWrapper> singleTypeImports;
    private Set<ImportWrapper> importOnDemandImports;

    @Override
    public Object visit(ASTCompilationUnit node, Object data) {
        singleTypeImports = new HashSet<>();
        importOnDemandImports = new HashSet<>();
        super.visit(node, data);

        // this checks for things like:
        // import java.io.*;
        // import java.io.File;
        for (ImportWrapper thisImportOnDemand : importOnDemandImports) {
            for (ImportWrapper thisSingleTypeImport : singleTypeImports) {
                String singleTypeFullName = thisSingleTypeImport.getFullName(); // java.io.File

                String singleTypePkg = thisSingleTypeImport.getPackageName(); // java.io
                String singleTypeName = thisSingleTypeImport.getName(); // File

                if (thisImportOnDemand.getFullName().equals(singleTypePkg)
                        && !isDisambiguationImport(node, singleTypePkg, singleTypeName)) {
                    addViolation(data, thisSingleTypeImport.getNode(), singleTypeFullName);
                }
            }
        }
        singleTypeImports.clear();
        importOnDemandImports.clear();
        return data;
    }

    /**
     * Check whether this seemingly duplicate import is actually a
     * disambiguation import.
     *
     * Example: import java.awt.*; import java.util.*; import java.util.List;
     * //Needed because java.awt.List exists
     */
    private boolean isDisambiguationImport(ASTCompilationUnit node, String singleTypePkg, String singleTypeName) {
        // Loop over .* imports
        for (ImportWrapper thisImportOnDemand : importOnDemandImports) {
            // Skip same package
            if (!thisImportOnDemand.getFullName().equals(singleTypePkg)) {
                if (!thisImportOnDemand.isStaticOnDemand()) {
                    String fullyQualifiedClassName = thisImportOnDemand.getFullName() + "." + singleTypeName;
                    if (node.getClassTypeResolver().classNameExists(fullyQualifiedClassName)) {
                        // Class exists in another imported package
                        return true;
                    }
                } else {
                    Class<?> importClass = node.getClassTypeResolver().loadClassOrNull(thisImportOnDemand.getFullName());
                    if (importClass != null) {
                        try {
                            for (Method m : importClass.getMethods()) {
                                if (Modifier.isStatic(m.getModifiers()) && m.getName().equals(singleTypeName)) {
                                    // static method in another imported class
                                    return true;
                                }
                            }
                        } catch (LinkageError e) {
                            // This is an incomplete classpath, report the missing class
                            LOG.log(Level.FINE, "Possible incomplete auxclasspath: Error while processing methods", e);
                        }
                    }
                }
            }
        }

        String fullyQualifiedClassName = "java.lang." + singleTypeName;
        // Class might exist in another imported package
        return node.getClassTypeResolver().classNameExists(fullyQualifiedClassName);
    }

    @Override
    public Object visit(ASTImportDeclaration node, Object data) {
        ImportWrapper wrapper = new ImportWrapper(node);

        // blahhhh... this really wants to be ASTImportDeclaration to be
        // polymorphic...
        if (node.isImportOnDemand()) {
            if (importOnDemandImports.contains(wrapper)) {
                addViolation(data, node, node.getImportedName());
            } else {
                importOnDemandImports.add(wrapper);
            }
        } else {
            if (singleTypeImports.contains(wrapper)) {
                addViolation(data, node, node.getImportedName());
            } else {
                singleTypeImports.add(wrapper);
            }
        }
        return data;
    }

}
